﻿
using Admin.NET.Core;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using IotContract.Enums;
using IotFun.Const;
using IotFun.Entity;
using IotFun.Views.EquipInfoView.Dto;
using IotFun.Views.EquipTagView.Dto;
using Magicodes.ExporterAndImporter.Core.Models;
using Magicodes.ExporterAndImporter.Excel;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Text.RegularExpressions;
using System.Text;
using Furion;

using Microsoft.Extensions.Primitives;
using IotContract.Models.Clients;
using IotFun.Services;
using Nest;
using XT.Common.Extensions;
using SqlSugar;
using IotContract.Models.Process.F29;
using Microsoft.AspNetCore.Authorization;

namespace IotFun.Views.EquipTagView;
/// <summary>
/// 设备点位服务
/// </summary>
[ApiDescriptionSettings(IotConst.GroupName)]
public class EquipTagService : IDynamicApiController, ITransient
{
    private readonly SqlSugarRepository<EquipTag> _rep;
    private readonly GrpcService _grpcService;
    public EquipTagService(SqlSugarRepository<EquipTag> rep,GrpcService grpcService)
    {
        _rep = rep;
        _grpcService = grpcService;
    }

    /// <summary>
    /// 分页查询设备点位
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "Page")]
    public async Task<SqlSugarPagedList<EquipTagOutput>> Page([FromQuery] EquipTagInput input)
    {
        var query = _rep.AsQueryable()
                    .WhereIF(!string.IsNullOrWhiteSpace(input.name), u => u.Name.Contains(input.name.Trim()) || u.TagProperty.Contains(input.name.Trim()) || u.TagNum.Contains(input.name.Trim()))

                    .LeftJoin<EquipInfo>((x, y) => x.EquipInfoId == y.Id)
                    .WhereIF(!string.IsNullOrWhiteSpace(input.equip), (x, y) =>y.IPAddress==input.equip.Trim() || y.Name.Contains(input.equip.Trim()) || y.Num.Contains(input.equip.Trim()))
                    .OrderBy(x => x.ReadWrite)
                    .OrderBy(x => SqlFunc.ToInt32(x.Address))
                    .Select((x, y) => new EquipTagOutput
                    {
                        id = x.Id,
                        remark = x.Remark,
                        equipname = y.Name,
                        name = x.Name
                    }, true);

      
        var datas= await query.ToPagedListAsync(input.Page, input.PageSize);


        datas.Items?.ForEach(item =>
        {

            item.datatypename = ((PlcDataType)item.datatype).ToString();
        });

        return datas;

    }

    /// <summary>
    /// 分页查询设备点位
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "PageData")]
    public async Task<SqlSugarPagedList<EquipTagIotOutput>> PageData([FromQuery] EquipTagInput input)
    {
        var query = _rep.AsQueryable()
                    .WhereIF(!string.IsNullOrWhiteSpace(input.name), u => u.Name.Contains(input.name.Trim()) || u.TagProperty.Contains(input.name.Trim()) || u.TagNum.Contains(input.name.Trim()))

                    .Where(x=>x.EquipInfoId==input.equipId)

                    .Select((x) => new EquipTagIotOutput
                    {
                        Id = x.Id,
                      ReadWrite= x.ReadWrite,
                      ReadBlock= x.ReadBlock,
                        DataLength=x.DataLength,
                        DataType=x.DataType,
                        
                        Address=x.Address,
                       
                        Name = x.Name,

                    }, true);


        var result = await query.ToPagedListAsync(input.Page, input.PageSize);

        if (result.Items.Count() == 0) return result;

      

        result.Items?.ForEach(item =>
        {
            
            item.DataTypeName = ((PlcDataType)item.DataType).ToString();
        });


        // 关联设备数据
        var tags = await _grpcService.GetEquipTags(new ClientRequestCommand
        {
            EquipId = input.equipId,
            ProtobufType = input.protobufType
        });

        if (tags == null)
        {
            return result;
        }
        result.Items.ForEach(item =>
        {
            var equip = tags.FirstOrDefault(x => x.Id == item.Id);
            if (equip != null)
            {
                item.ReadTime = equip.ReadTime;
                item.Value = equip.Value;
                item.OldValue = equip.OldValue;
                item.ReadChangeTime = equip.ReadChangeTime;
              
            }
        });

        return result;





    }

 

    /// <summary>
    /// 写入点位
    /// </summary>
    /// <param name="writeDto"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "WriteTag")]
    public async Task WriteTag(EquipTagWriteDto writeDto)
    {
        List<OperationWriteCommand> commands = new List<OperationWriteCommand>();
        // 扫码逻辑
        if (writeDto.Num.IsNotNullOrEmpty() && writeDto.Mode==100)
        {
            //var equips = await _rep.Context.Queryable<EquipInfo>() .Where(x => x.UnitNum == writeDto.Num.ToString()).ToListAsync();
            //if(equips==null || equips.Count == 0)
            //{
            //    throw Oops.Oh("no unitnum bcr,write error");
            //}
            //foreach(var equip in equips)
            //{
            //    commands.Add(new OperationWriteCommand
            //    {
            //        EquipId = equip.Id,
            //        ProtobufType = writeDto.ProtobufType,                  
            //        Value = "1"
            //    });
            //}

            // 扫码逻辑
            await _grpcService.ExecuteProcessData(new ProcessJobCommand
            {
                ProviderType = ProcessProviderType.Cv,
                Job = new F29ProcessCvData
                {
                    Num = writeDto.Num,

                }
            });
            return;
        }
        else
        {
            var names = writeDto.Names.Split(",");
            var datas = writeDto.Datas.Split(",");
            if (names.Length != datas.Length)
            {
                throw Oops.Oh("write data length error");
            }
            List<EquipTag> tags = new List<EquipTag>();
            if (writeDto.Mode == 0)
            {
                tags = await _rep.AsQueryable().Where(x => x.EquipInfoId == writeDto.EquipId).Where(x => names.Contains(x.Address)).WhereIF(writeDto.Num.IsNotNullOrEmpty(),x=>x.TagNum==writeDto.Num.ToString()).ToListAsync();
            }
            else if(writeDto.Mode == 1)
            {
                tags = await _rep.AsQueryable().Where(x => x.EquipInfoId == writeDto.EquipId).Where(x => names.Contains(x.TagProperty)).WhereIF(writeDto.Num.IsNotNullOrEmpty(), x => x.TagNum == writeDto.Num.ToString()).ToListAsync();
            }
            else
            {
                tags = await _rep.AsQueryable().Where(x => x.EquipInfoId == writeDto.EquipId).Where(x => names.Contains(x.TagProperty)).ToListAsync();

            }

            if (tags.Count == 0)
            {
                throw Oops.Oh("write data error");
            }

            // 顺序不能打乱
            for (int i = 0; i < datas.Length; i++)
            {
                EquipTag tag = new EquipTag();
                if (writeDto.Mode == 0)
                {
                    tag = tags.FirstOrDefault(x => x.Address == names[i]);
                }
                else
                {


                    tag = tags.FirstOrDefault(x => x.TagProperty == names[i]);
                }

                if (tag == null) continue;

                commands.Add(new OperationWriteCommand
                {
                    EquipId = tag.EquipInfoId,
                    ProtobufType = writeDto.ProtobufType,
                    TagId = tag.Id,
                    Value = datas[i]
                });
            }
            if (commands.Count == 0)
            {
                throw Oops.Oh("wirte no tag");
            }
        }
      
       
       
      var result= await _grpcService.WriteEquips(commands);
        if (!result)
        {
            throw Oops.Oh("wirte call fail");
        }
       
    }
    /// <summary>
    /// 增加设备点位
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "Add")]
    public async Task Add(AddEquipTagInput input)
    {
        var entity = input.Adapt<EquipTag>();
       long id= await _rep.AsQueryable().Where(x => x.TagProperty == input.tagproperty && x.EquipInfoId == input.equipinfoid && x.TagNum==input.tagnum).Select(x => x.Id).FirstAsync();

        if (id > 0)
        {
            throw Oops.Oh("repeat tagProperty");
        }
        await _rep.InsertAsync(entity);
    }

    /// <summary>
    /// 批量增加设备点位
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "AddByEquipName")]
    [AllowAnonymous]
    public async Task AddByEquipName(EquipTagAllDto input)
    {
        var names = input.EquipNames.Split(',');
        var entity = input.Adapt<EquipTag>();

        foreach (var name in names)
        {
           var equipId= await _rep.Context.Queryable<EquipInfo>().Where(x => x.Name == name).Select(x => x.Id).FirstAsync();
            if (equipId == 0)
            {
                throw Oops.Oh(name + " 设备不存在");
            }
            entity.EquipInfoId = equipId;
            entity.Id = 0;
            await _rep.InsertAsync(entity);
        }
     
       
       
    }

    /// <summary>
    /// 删除设备点位
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "Delete")]
    public async Task Delete(DeleteEquipTagInput input)
    {
        var entity = await _rep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
        await _rep.DeleteAsync(entity);   //假删除
    }

    /// <summary>
    /// 更新设备点位
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "Update")]
    public async Task Update(UpdateEquipTagInput input)
    {
        var entity = input.Adapt<EquipTag>();
        long id = await _rep.AsQueryable().Where(x => x.TagProperty == input.tagproperty && x.EquipInfoId == input.equipinfoid && x.TagNum==input.tagnum && x.Id!=input.id).Select(x => x.Id).FirstAsync();

        if (id > 0 )
        {
            throw Oops.Oh("repeat tagProperty");
        }
        await _rep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();

    }

    /// <summary>
    /// 获取设备点位
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "Detail")]
    public async Task<EquipTag> Get([FromQuery] QueryByIdEquipTagInput input)
    {
        return await _rep.GetFirstAsync(u => u.Id == input.Id);
    }
    /// <summary>
    /// 根据tag点位获取设备id
    /// </summary>
    /// <param name="tagNum"></param>
    /// <returns></returns>
    public async Task<long> GetEquipIdByTagNum(string tagNum)
    {
       return await _rep.AsQueryable().Where(x => x.TagNum == tagNum).Select(x => x.EquipInfoId).FirstAsync();
    }

    /// <summary>
    /// 获取设备点位列表
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "List")]
    public async Task<List<EquipTagOutput>> List([FromQuery] EquipTagInput input)
    {
        return await _rep.AsQueryable().Select<EquipTagOutput>().ToListAsync();
    }

    /// <summary>
    /// 获取点位数据类型
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "Type")]
    public async Task<dynamic> GetDataTypes()
    {
        var codes= PlcDataType.BIT.ToKeyCodeList();
        return codes;
            
    }

    /// <summary>
    /// 下载设备管理导入模板
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "template")]
    public async Task<string> ExportEquipTagTemplate()
    {
        var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "点位导入模板.xlsx");
        if (!File.Exists(filePath))
        {
            throw Oops.Oh("未配置模板，无法导出");
        }
        return new FileInfo(filePath).Name;
    }

    /// <summary>
    /// 导出点位信息
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "export")]
    public async Task<string> ExportEquipTag([FromQuery] EquipTagInput input)
    {
        var query = _rep.AsQueryable()
                    .WhereIF(!string.IsNullOrWhiteSpace(input.name), u => u.Name.Contains(input.name.Trim()) || u.TagProperty.Contains(input.name.Trim()) || u.TagNum.Contains(input.name.Trim()))

                    .LeftJoin<EquipInfo>((x, y) => x.EquipInfoId == y.Id)
                    .WhereIF(!string.IsNullOrWhiteSpace(input.equip), (x, y) => y.Name.Contains(input.equip.Trim()) || y.Num.Contains(input.equip.Trim()))
                     .LeftJoin<SysDictData>((x, y, m) => x.DataType.ToString() == m.Value)
                    .LeftJoin<SysDictType>((x, y, m, n) => m.DictTypeId == n.Id && n.Code == "equiptag_datatype")
                    .Select((x, y,m,n) => new EquipTagOutput
                    {
                        id = x.Id,
                        remark = x.Remark,
                        equipname = y.Name,
                        name = x.Name,
                        datatypename = m.Code
                    }, true);

        var datas = await query.ToListAsync();

        foreach (var data in datas)
        {
            data.datatypename = Enum.GetName(typeof(PlcDataType), data.datatype);
        }

        if (datas.Count == 0)
        {
            throw Oops.Oh("没有数据，无法导出");
        }
        var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "点位导出.xlsx");

        if (File.Exists(filePath)) File.Delete(filePath);

        var exports = datas.Adapt<List<EquipTagOutput>>();

        var result = await new ExcelExporter().Export(filePath, exports);

        return new FileInfo(filePath).Name;
    }

    /// <summary>
    /// 从模板导入设备管理
    /// </summary>
    /// <param name="formCollection">前端上传的文件集合</param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "import")]
    public async Task<string> Upload([FromForm] IFormCollection formCollection)
    {
        if (formCollection == null || formCollection.Files.Count <= 0 || formCollection.Files.Count <= 0)
        {
            throw Oops.Oh("导入异常，检查导入文件数据");
        }
        var fileCollection = (FormFileCollection)formCollection.Files;
        foreach (IFormFile file in fileCollection)
        {
            ImportResult<EquipTagOutput> importRes;
            ExcelImporter excelImporter = new ExcelImporter();

            using (Stream memory = new System.IO.MemoryStream())
            {
                file.CopyTo(memory);
                importRes = await excelImporter.Import<EquipTagOutput>(memory);
            }
            //导入校验
            if (!importRes.IsNotNull() || importRes.Exception != null)
            {
                throw Oops.Oh("导入异常，检查导入数据");
            }
            else if (importRes.RowErrors.Count > 0 || importRes.HasError)
            {
                StringBuilder rowErrors = new StringBuilder();
                for (int i = 0; i < importRes.RowErrors.Count; i++)
                {
                    rowErrors.Append("第" + importRes.RowErrors[i].RowIndex + "行，");
                    foreach (var headerName in importRes.RowErrors[i].FieldErrors)
                    {
                        rowErrors.Append(headerName.Key + "，");
                    }
                    rowErrors.Append("数据格式错误！");
                }
                throw Oops.Oh(rowErrors.ToString());
            }
            else//数据校验及导入
            {
                List<EquipTag> list = new List<EquipTag>();
                StringBuilder exceptions = new StringBuilder();
                List < EquipTagOutput > tagList = importRes.Data.ToList();
                StringBuilder rowErrors = new StringBuilder();
                for (int i = 0; i < tagList.Count; i++)
                {
                    for(int j = i+1;j < tagList.Count; j++)
                    {
                        if (tagList[j].equipname == tagList[i].equipname 
                            && tagList[j].readblock == tagList[i].readblock 
                            && tagList[j].address == tagList[i].address
                            && tagList[j].name == tagList[i].name)
                        {
                            rowErrors.Append("第" + (i+2) + "行与第" + (j+2) + "行,");
                        }
                    }
                }
                if (rowErrors.Length > 0)
                {
                    rowErrors.Append("设备、读写块、读写地址、Tag名称重复！");
                    throw Oops.Oh(rowErrors.ToString());
                }
                for (int i = 0; i < tagList.Count; i++)
                {
                    EquipTagOutput item = tagList[i];
                    StringBuilder ex = new StringBuilder();
                    var equipTag = new EquipTag();

                    //检查设备ID和设备名称是否在系统中存在
                    List<EquipInfo>? equipInfoList;
                    if (string.IsNullOrWhiteSpace(item.equipname) )
                    {
                        equipInfoList = _rep.Context.Queryable<EquipInfo>()
                                        .Where(x => item.equipinfoid == x.Id).ToList();
                    }
                    else
                    {
                        equipInfoList = _rep.Context.Queryable<EquipInfo>()
                                       .Where(x => item.equipname == x.Name && item.equipinfoid == x.Id).ToList();
                    }
                    
                    if (equipInfoList == null || equipInfoList.Count <= 0)
                    {
                        ex.Append("设备系统中不存在，");
                    }
                    //检查点位数据类型
                    string dataTypeName = Enum.GetName(typeof(PlcDataType), item.datatype);
                    if (dataTypeName.IsNullOrEmpty())
                    {
                        ex.Append("点位数据类型系统中不存在，");
                    }
                    if(dataTypeName != item.datatypename)
                    {
                        ex.Append("点位数据类型代码和名称对应错误，");
                    }
                    //校验设备、块、地址与已存在的数据是否重复
                    var res1= _rep.AsQueryable().Where(x => x.Name == item.name && x.EquipInfoId == equipInfoList[0].Id && x.Address == item.address && x.ReadBlock == item.readblock).ToList();
                    if(res1.Count > 0)
                    {
                        ex.Append("Tag名称：" + item.name +  "，设备：" + item.equipinfoid + "，读写块：" + item.readblock + "，地址："+ item.address + "，系统已存在！");
                    }
                    if (ex.Length > 0)
                    {
                        string exStr = ex.ToString();
                        exceptions.Append("第" + (i + 2) + "行，" + exStr.Substring(0, exStr.Length - 1) + "；");
                        continue;
                    }
                    equipTag.Name = item.name;
                    equipTag.TagNum = item.tagnum;
                    equipTag.TagProperty = item.tagproperty;
                    equipTag.Address = item.address;
                    equipTag.DataType = item.datatype;
                    equipTag.DataLength = item.datalength;
                    equipTag.EquipInfoId = equipInfoList[0].Id;
                    equipTag.ReadWrite = item.readwrite;
                    equipTag.ReadBlock = item.readblock;
                    equipTag.IsEnable = item.isenable;
                    equipTag.Expression = item.expression;
                    equipTag.Remark = item.remark;

                    list.Add(equipTag);
                }
                if (list.Count > 0 && exceptions.Length == 0)
                {
                    //SQLSugar写入数据
                    await _rep.InsertRangeAsync(list);
                    return "导入完成！";
                }
                throw Oops.Oh("导入异常！" + exceptions);
                //return "导入异常！" + exceptions;
            }
        }
        throw Oops.Oh("导入异常！");
    }

}

